(function(angular) {
	'use strict';
	angular.module("fs.waf").factory('dateService', [ '$filter', 'storageService', 'formatService',  function($filter, storageService, formatService) {

		
		/**
		 * function list
		 * 1. isHoliday(nowDate,objHdArry)		//input time is holiday (no Saturday and  no Sunday)
		 * 2. getEndDate(nowDate, addnum)		//get maturity date from begin date and tenor (no care about if the maturity date is holiday)
		 * 3. getEeffectEndDate(nowDate, addnum,objHdArry)	//get maturity date from begin date and tenor (if the maturity date is holiday then it postpones until it isnot holiday)
		 * 4. getDateOffset(beginDate,endDate)  //get tenor between begin date and maturity date (nature day)
		 * 5. getEeffectDateOffset(beginDate,endDate,objHdArry)  //get tenor between begin date and maturity date (only workday)
		 * 6. isSatOrSunDay(nowDate)   //the date is Saturday or Sunday
		 */
		/**
		 * @initial month array
		 * @params 
		 * @return 
		 */
		function _Months()
		{
		    this[0] = "Jan"; 	this[1] = "Feb";	this[2]="Mar";
			  this[3] = "Apr";	this[4] = "May"; 	this[5]="Jun";
			  this[6] = "Jul";	this[7] = "Aug";	this[8]="Sep";
			  this[9] = "Oct";	this[10]= "Nov";	this[11]="Dec";
		}
		//define month array
		var mons = new _Months();

		/**
		 * @input time is holiday (no Saturday and  no Sunday)
		 * @params nowDate - date, formatter is :2003-Jul-29
		 * @params objHdArry - holiday array, formatter is :objHdArry[0] = "2003-Jan-01",objHdArry[1] = "2003-Jan-07"...
		 * @return true or false
		 */
		function isHoliday(nowDate,objHdArry){
		    for(var tmp in objHdArry) {
		        if (compareDate(objHdArry[tmp], nowDate) == 0) {
		            return true;              
		        }                                     
		    }
		    return false;
		}

		/**
		 * @ get maturity date from begin date and tenor (no care about if the maturity date is holiday)
		 * @param nowDate - date, formatter is :2003-Jul-29 or 2003-01-29
		 * @param space - tenor, integer 
		 * @return String, formatter is :2003-01-29
		 */
		function getEndDate(nowDate, space) {
		    var curryDate;
		    var endDate;
		    
		    curryDate = parseDate(nowDate);
		    //get maturity date
		    space = parseInt(space);
		    curryDate.setDate(curryDate.getDate() + space);
		    
		    //transform the format
			return formatService.formatDate(curryDate, 'date');
		}
		
		
		/**
		 * @ get maturity date from begin date and tenor (no care about if the maturity date is holiday)
		 * @param nowDate - date, formatter is :2003-Jul-29 or 2003-01-29
		 * @param space - tenor, integer 
		 * @return Date, no format
		 */
		function addDays(nowDate, space) {
		    var curryDate;
		    var endDate;
		    
		    curryDate = parseDate(nowDate);
		    //get maturity date
		    space = parseInt(space);
		    curryDate.setDate(curryDate.getDate() + space);
		    
			return curryDate;
		}
		

		/**
		 * @get maturity date from begin date and tenor (if the maturity date is holiday then it postpones until it isnot holiday)
		 * @params nowDate - date, formatter is :2003-Jul-29
		 * @params addnum - tenor, integer 
		 * @params objHdArry - date, formatter is :objHdArry[0] = "2003-Jan-01",objHdArry[1] = "2003-Jan-07"...
		 * @return: the maturity date that is not holiday, it's formatter is :2003-Jul-29
		 */
//		function getEeffectEndDate(nowDate, addnum,objHdArry){	
//		    var endDate=getEndDate(nowDate, addnum);    
//		    var _addnum=addnum-0;
//		    while(isHoliday(endDate,objHdArry) || isSatOrSunDay(endDate)){
//		    	_addnum=_addnum+1;
//		    	endDate=getEndDate(nowDate, _addnum);
//		    }
//		    return endDate;
//		}
		
		/**
		 * get effective end date
		 * @params nowdate - begin date
		 * @params addnum - tenor
		 * @params objhdarry - holiday array
		 * @params weekEnd - weekend array
		 * @params direct - forward or backword when the end day is holiday
		 */
		function getEeffectEndDate(nowDate,addnum,objHdArry,weekEnd,direct){
			var endDate=getEndDate(nowDate, addnum);    
		    var _addnum=addnum-0;
			
//		    while(isHoliday(endDate,objHdArry) || isWeekend(endDate, weekEnd)){
		    // objHdArry is date, so need change endDate to date
		    while(isHoliday(parse(endDate),objHdArry)){
		    	if (direct=="N")
			    	_addnum=_addnum+1;
			    else
			    	_addnum=_addnum-1;
		    	endDate=getEndDate(nowDate, _addnum);
		    }
		    return endDate;
		}

		/**
		 * @get tenor between begin date and maturity date (nature day)
		 * @params beginDate - begin date, formatter is :2003-Jul-29
		 * @params endDate - end date, formatter is :2003-Jul-29
		 * @return: the number of nature days betwwen beginDate and endDate
		 */
		function getDateOffset(beginDate, endDate){
			var pd1 = parseDate(beginDate);
			var pd2 = parseDate(endDate);
			var MinMilli = 1000 * 60;
			var HrMilli = MinMilli * 60;
			var DyMilli = HrMilli * 24;
			var difference =
		        Date.UTC(pd2.getFullYear(), pd2.getMonth(), pd2.getDate())
		      - Date.UTC(pd1.getFullYear(), pd1.getMonth(), pd1.getDate());
		    return (difference / DyMilli);
		}

		/**
		 * @get tenor between begin date and maturity date (workday)
		 * @params beginDate - begin date, formatter is :2003-Jul-29
		 * @params endDate - end date, formatter is :2003-Jul-29
		 * @params beginDate - holiday array, formatter is :objHdArry[0] = "2003-Jan-01",objHdArry[1] = "2003-Jan-07"...
		 * @return: the number of valid workdays betwwen beginDate and endDate
		 */
		function getEeffectDateOffset(beginDate,endDate,objHdArry){

		    var total=getDateOffset(beginDate,endDate);
		    var hoilday=0;
		    for(var k=0;k<=total;k++){
		        if (isHoliday(getEndDate(beginDate,k+0),objHdArry) || isSatOrSunDay(getEndDate(beginDate,k+0)))
		            hoilday=hoilday+1;
		    }    
		    return total-hoilday;
		}

		/**
		 * If the date is weekend.
		 * @param strDate - String, formater is :"2003-Jul-29" or "2005-10-10"
		 * @return boolean
		 */
		function isWeekend(strDate){
		    if(!strDate || strDate == "") {
		        return false;
		    }

		    var curryDate = parseDate(strDate);
		    if(curryDate == null) {
		        return false;
		    }
		    return (curryDate.getDay() == 0 || curryDate.getDay() == 6);
		}

		/**
		 * 
		 * @param strDateStarting String. "2005-10-10"
		 * @param strDateEnd String. "2005-10-10"
		 * @return int. if value > 0, the starting date is after the end date; if <, the starting date is before the end date. if =, the same date.
		 */
		function compareDate(strDateStarting, strDateEnd) {
			var startDate = parse(strDateStarting);
			var endDate = parse(strDateEnd);
			return getDaysBetweenDates(startDate, endDate);
		}

		/**
		 * 
		 * @param strDate String. "2005-10-10"
		 * @return Date
		 */
		function parseDate(strDate, fmtStr) {
		    return parse(strDate, fmtStr);
		}

		/**
		 * Get the last day of month which the assigned date is in.
		 * @param assignedDate - String. "2005-10-10"
		 * @return Date - String. "2005-10-31"
		 * @since 2007.5.23
		 */
		function getLastDayOfMonth(assignedDate)  {
		    var dayOfMonth;
		    assignedDate = parseDate(assignedDate);
		    var month = assignedDate.getMonth()+1;
		    var year  = assignedDate.getFullYear();

		    // RETURN 31 DAYS
		    if (month==1 || month==3 || month==5 || month==7 || month==8 ||
		        month==10 || month==12)  {
		        dayOfMonth=31;
		    }
		    // RETURN 30 DAYS
		    else if (month==4 || month==6 || month==9 || month==11) {
		        dayOfMonth=30;
		    }
		    // RETURN 29 DAYS
		    else if (month==2)  {
		        if (isLeapYear (year)) {
		            dayOfMonth=29;
		        }
		        // RETURN 28 DAYS
		        else {
		            dayOfMonth=28;
		        }
		    }
		    //get maturity date
		    assignedDate.setDate(dayOfMonth);
		    //transform the format
		    return formatService.formatDate(assignedDate, 'date');
		}
		/**
		 * CHECK TO SEE IF YEAR IS A LEAP YEAR.
		 * @param Year - integer. 
		 * @return true - leap year. false - not leap year
		 * @since 2007.5.23
		 */
		function isLeapYear (Year) {
		    if (((Year % 4)==0) && ((Year % 100)!=0) || ((Year % 400)==0)) {
		        return (true);
		    }
		    else {
		        return (false);
		    }
		}
		
		function parse(dateStr, fmtStr){
			if (!dateStr || angular.isDate(dateStr)) return dateStr;
			return formatService.parseDate(dateStr, fmtStr || 'date');
		}

		/**
		 * Validate date String with format string.
		 * @param dateStr 
		 * @param fmtStr
		 * @return the value true if the date string is legal;
		 *		   a value false if this Date is illegal; 
		 */
		function validate(dateStr, fmtStr) {
			return (parse(dateStr, fmtStr) != null);
		}

		/**
		 * Compares two dates for equality.
		 * @param date1
		 * @param date2
		 * @return the value true if the argument Date is equal to this Date;
		 */
		function compare(date1, date2) {
			if (!date1 || !date2) return null;
			var time1 = date1.getTime();
			var time2 = date2.getTime();
			if(time1 > time2){
				return -1;
			} else if (time1 < time2) {
				return 1;
			} else {
				return 0;
			}
		}

		/**
		 * calculate number of days between two dates, beginDate (included) to endDate (excluded).
		 * @param endDate the end date of the period.
		 * @param beginDate the begin date of the period
		 * @return number of days from beginDate (included) to endDate (excluded)
		 */
		function getDaysBetweenDates (beginDate, endDate) {
			if (!beginDate || !endDate) return null;
			var beginTime = beginDate.getTime();
			var endTime = endDate.getTime();
			var oneDay = 24 * 60 * 60 * 1000;
			return Math.ceil((endTime - beginTime)/oneDay);
		}

		/**
		 * Compares two dates for equality.
		 * @param date1
		 * @param date2
		 * @return the value true if the argument Date is equal to this Date;
		 */
		function equals(date1, date2) {
			if (!date1 || !date2) return false; 
			return date1.getTime() == date2.getTime();
		}

		/**
		 * Compares two dates for equality. ignoring time considerations. 
		 * @param date1
		 * @param date2
		 * @return the value true if the argument Date is equal to this Date;
		 * @see #equals(date1, date2);
		 */
		function equalsIgnoreTime(date1, date2) {
			//Clear the date. set hour, minute, second and millisecond to 0.
			this.clearTime = function(targetDate) {
				targetDate.setHours(0);
				targetDate.setMinutes(0);
				targetDate.setSeconds(0);
				targetDate.setMilliseconds(0);
				return targetDate;
			};
			var date1 = this.clearTime(date1);
			var date2 = this.clearTime(date2);
		 	return equals(date1, date2);
		}

		/**
		 * Date Arithmetic function. Adds the specified (signed) amount of time to the given time field.
		 * For example:  add(new Date(), 'M', -5)
		 *				 add(new Date(), 'y', 3)
		 * @param date
		 * @param type
		 * @param interval  
		 * @return date;
		 */
		function add(date, type, interval) {
			if(!date || typeof(date) != 'object') return null;
			if(!type || !interval) return date;
			interval = parseInt(interval);
			if (type == 'y') {
			  	date.setFullYear(date.getFullYear() + interval);
			} else if (type == 'M') {
		  		date.setMonth(date.getMonth() + interval);
			} else if (type == 'd') {
		  		date.setDate(date.getDate() + interval);
			} else if (type == 'h') {
		  		date.setHours(date.getHours() + interval);
			} else if (type == 'm') {
				date.setMinutes(date.getMinutes() + interval);
			} else if (type == 's') {
		  		date.setSeconds(date.getSeconds() + interval);
			}
			return date;
		}
		
		
		return {
			isHoliday : isHoliday,
			getEndDate : getEndDate,
			addDays : addDays,
			getEeffectEndDate : getEeffectEndDate,
			getDateOffset : getDateOffset,
			getEeffectDateOffset : getEeffectDateOffset,
			isWeekend:isWeekend,
			isLeapYear:isLeapYear,
			getLastDayOfMonth:getLastDayOfMonth,
			compareDate:compareDate,
			equalsIgnoreTime: equalsIgnoreTime,
			parseDate:parseDate
		};
	} ]);
})(angular);
